using System;
using System.Linq;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using Bazookabird.Wpf.Model;
using Caliburn.Micro;
using Microsoft.TeamFoundation.WorkItemTracking.Client;

namespace Bazookabird.Wpf.Queries.QueryHandlers.SimpleSprintViewer
{
    public class WorkItemTimelineCollection : ObservableCollection<WorkItemTimeline>
    {
        public WorkItemTimelineCollection(IEnumerable<WorkItemNode> workItemNodes, DateTime startDate, DateTime endDate)
        {
            foreach (var workitem in workItemNodes)
            {
                Add(new WorkItemTimeline(workitem, startDate, endDate));
            }

            WorkItemDateCollection = new WorkItemDateCollection(this, true);
        }

        public WorkItemDateCollection WorkItemDateCollection { get; private set; }

        public double? GetMaxScale() 
        {
            if (WorkItemDateCollection == null) return null;
            return WorkItemDateCollection
                .Select(w => w.SumHoursRemaining)
                .Max();
        }
    }

    public class WorkItemDateCollection : ObservableCollection<WorkItemDate>
    {
        public WorkItemDateCollection(IEnumerable<WorkItemTimeline> workItemTimelineCollection, bool disableFutureDates)
        {
            foreach (var workItemTimeline in workItemTimelineCollection)
            {
                AddItems(workItemTimeline, disableFutureDates);
            }
        }

        private void AddItems(WorkItemTimeline workItemTimeline, bool disableFutureDates)
        {
            foreach (var revision in workItemTimeline.Timeline)
            {
                var collection = GetOrCreateDateCollectionDate(revision);

                if (!ShouldAdd(disableFutureDates, revision)) continue;
                
                AddHoursIfExist(collection, revision);
            }
        }

        private static void AddHoursIfExist(WorkItemDate collection, KeyValuePair<DateTime, Revision> workItem)
        {
            var hours = GetHoursRemaining(workItem);
            if (hours == null) return;
            
            if (collection.SumHoursRemaining == null)
            {
                collection.SumHoursRemaining = 0;
            }

            collection.SumHoursRemaining += hours.Value;
        }

        private static bool ShouldAdd(bool disableFutureDates, KeyValuePair<DateTime, Revision> workItem)
        {
            var hasWorkItem = workItem.Value != null;
            var isFutureDate = workItem.Key >= DateTime.Now.Date.AddDays(1);
            var isDateOk = !(disableFutureDates && isFutureDate);
            
            return hasWorkItem && isDateOk;
        }

        private WorkItemDate GetOrCreateDateCollectionDate(KeyValuePair<DateTime, Revision> revision)
        {
            var collection = this.FirstOrDefault(i => i.Date.Date == revision.Key);
            if (collection == null)
            {
                collection = new WorkItemDate { Date = revision.Key};
                Add(collection);
            }
            return collection;
        }

        private static double? GetHoursRemaining(KeyValuePair<DateTime, Revision> revision)
        {
            return GetFieldValueAsDouble(revision, "Microsoft.VSTS.Scheduling.RemainingWork");
        }

        private static double? GetFieldValueAsDouble(KeyValuePair<DateTime, Revision> workItem, string fieldKey)
        {
            if(workItem.Value.Fields.Contains(fieldKey))
            {
                var field = workItem.Value.Fields[fieldKey];            
                if (field != null && field.Value != null)
                {
                    return double.Parse(field.Value.ToString());
                }
            }
            return null;
        }
    }

    public class WorkItemDate : PropertyChangedBase
    {
        private DateTime _date;
        public DateTime Date
        {
            get { return _date; }
            set
            {
                _date = value;
                NotifyOfPropertyChange(() => Date);
            }
        }

        private double? _sumHoursRemaining;
        public double? SumHoursRemaining
        {
            get { return _sumHoursRemaining; }
            set
            {
                _sumHoursRemaining = value;
                NotifyOfPropertyChange(() => SumHoursRemaining);
            }
        }
    }
}